package cn.huijielong.common.utils;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.huijielong.common.exception.HttpClientException;


public class HttpClientRequest {
	
	public final String name="xt.http.client";
	
	private static HttpClientRequest instance=null; 
	private static Object locked=new Object();

    public interface ResultListener {


        public void onConnectionPoolTimeoutError();

    }

    private static Logger log=LoggerFactory.getLogger(HttpClientRequest.class);

    //表示请求器是否已经做了初始化工作
    private boolean hasInit = false;

    //连接超时时间，默认20秒
    private int socketTimeout = 20000;

    //传输超时时间，默认60秒
    private int connectTimeout = 60000;
    
    //连接不够用的时候获取连接等待超时时间，这个值不要设得太大
    private int requestTimeout=2000;
    
    //每个目标主机上的最大连接值(如http://10.51.132.121，这个连接请求最大为2)，默认是2个
    private int maxConnPerRoute=50;
    
    //整个池子的最大连接值，默认是20个
    private int maxConnTotal=500;

    //请求器的配置
    private RequestConfig requestConfig;

    //HTTP请求器
    private CloseableHttpClient httpClient;

    private HttpClientRequest(int socketTimeout,int connectTimeout,int requestTimeout) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
    	this.socketTimeout=socketTimeout;
    	this.connectTimeout=connectTimeout;
    	this.requestTimeout=requestTimeout;
    	ClientManager.addClient(this);
    	init();
    }
    private HttpClientRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
    	this(20000, 60000, 2000);
    }
    /**
     * 获取一个单例类，这个用在使用不是很频繁的http连接处理
     * @return
     */
    public static HttpClientRequest getInstance(){
    	if(instance==null){
    		synchronized(locked){
    			if(instance==null){
    				instance=HttpClientRequest.newClient(); 
    			}
    		}
    	}
    	return instance;
    }
    /**
     * 获取一个新的实例类，为使用很频繁的http连接单独建一个连接池
     * @return
     */
    public static HttpClientRequest newClient(int socketTimeout,int connectTimeout,int requestTimeout){
    	try {
			return new HttpClientRequest(socketTimeout,connectTimeout,requestTimeout);
		} catch (Exception e) {
			log.error("创建httpclient出错", e);
			return null;
		}
    }
    public static HttpClientRequest newClient(){
    	try {
    		return new HttpClientRequest();
    	} catch (Exception e) {
    		log.error("创建httpclient出错", e);
    		return null;
    	}
    }

    private void init() throws HttpClientException {

    	try{
    		httpClient = HttpClients.custom().
	        		setMaxConnPerRoute(maxConnPerRoute). //每个目标主机上的最大连接值(如http://10.51.132.121，这个连接请求最大为2)，默认是2个
	        		setMaxConnTotal(maxConnTotal).  //整个池子的最大连接值，默认是20个
	        		build();
	
	        //根据默认超时限制初始化requestConfig
	        resetRequestConfig();
	
	        hasInit = true;
    	}catch(Exception ex){
    		log.error("Http客户端连接初始化失败:", ex);
    		throw new HttpClientException("Http客户端连接初始化失败").setName(name);
    	}
    }


    public String httpGet(String url) throws HttpClientException {

        if (!hasInit) {
            init();
        }
        long startTime =System.currentTimeMillis();
        String result = null;

        HttpGet httpPost = new HttpGet(url);
        CloseableHttpResponse response=null;

        //设置请求器的配置
        httpPost.setConfig(requestConfig);

        log.info("executing request" + httpPost.getRequestLine());

        try {
            response = httpClient.execute(httpPost);

            HttpEntity entity = response.getEntity();

            result = EntityUtils.toString(entity, "UTF-8");
            log.info("本次http请求发送完成,共耗时间：{}，返回内容：{}",(System.currentTimeMillis()-startTime),result);
        } catch (Exception e) {
            log.error("发送Get请求共耗时间："+(System.currentTimeMillis()-startTime)+"，发送失败："+url,e);
            throw new HttpClientException("发送Get请求失败：["+url+"]").setName(name);
        } finally {
        	try {
        		if(httpPost!=null)
                	httpPost.abort();
			} catch (Exception e2) {
			}
            try {
        		if(response!=null)
        			response.close();
			} catch (Exception e2) {
			}
        }

        return result;
    }
    public String sendPost(String url, String params) throws HttpClientException{
    	//text/json,text/xml
    	return sendPost(url, params, "text/json",null);
    }
    
    public String sendPost(String url, String params,String ContentType) throws HttpClientException {
    	return sendPost(url, params, ContentType, null);
    }
    
    public String sendPost(String url, String params,String ContentType,String Accept) throws HttpClientException {

        if (!hasInit) {
            init();
        }
        long startTime =System.currentTimeMillis();
        String result = null;

        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response=null;

        log.info("远程请求，POST过去的数据是：{}",params);

        //得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(params, "UTF-8");
        httpPost.addHeader("Content-Type", ContentType);
        if(Accept!=null){
        	httpPost.addHeader("Accept", Accept);
        }
        httpPost.setEntity(postEntity);

        //设置请求器的配置
        httpPost.setConfig(requestConfig);

        try {
        	response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
            log.info("本次POST到[{}]http请求完成,共耗时间：{}",url,(System.currentTimeMillis()-startTime));
        } catch (Exception e) {
            log.error("发送POST请求共耗时间："+(System.currentTimeMillis()-startTime)+"，发送失败："+url, e);
            throw new HttpClientException("发送POST请求失败：["+url+"]").setName(name);
        } finally {
            try {
            	if(httpPost!=null)
                	httpPost.abort();
			} catch (Exception e2) {
			}
            try {
        		if(response!=null)
        			response.close();
			} catch (Exception e2) {
			}
        }

        return result;
    }
    
    public String sendPost(String url, Map<String,String> params,String ContentType) throws HttpClientException {

    	if (!hasInit) {
            init();
        }
        long startTime=System.currentTimeMillis();
        String result = null;

        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response=null;

        log.info("远程请求，POST过去的数据是：{}",params);

        //设置请求器的配置
        List <NameValuePair> nvps = new ArrayList <NameValuePair>();
        if (params != null) {  
            for (Map.Entry<String, String> entry:params.entrySet()) { 
            	nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }  
        }  
        if (ContentType!=null && !"".equals(ContentType)) {
        	httpPost.addHeader("Content-Type", ContentType);
		}
        try {
        	httpPost.setEntity(new UrlEncodedFormEntity(nvps,"UTF-8"));
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
            log.info("本次POST到[{}]http请求完成,共耗时间：{}",url,(System.currentTimeMillis()-startTime));
        } catch (Exception e) {
            log.error("发送POST请求共耗时间："+(System.currentTimeMillis()-startTime)+"，发送失败："+url+"=>>>"+params, e);
            throw new HttpClientException("发送POST请求失败：["+url+"]").setName(name);
        } finally {
        	try {
        		if(httpPost!=null)
            		httpPost.abort();
			} catch (Exception e2) {
			}
        	try {
        		if(response!=null)
        			response.close();
			} catch (Exception e2) {
			}
        }
        return result;
    }
    
    public String sendPost(String url, Map<String,String> params){
    	return sendPost(url,params,"text/html");
    }

    /**
     *
     * @param socketTimeout 设置连接超时时间, 连接时长，默认20秒
     * @param connectTimeout 设置传输超时时间, 传输时长，默认60秒
     */
    public void setSocketTimeout(int socketTimeout,int connectTimeout) {
        this.socketTimeout = socketTimeout;
        this.connectTimeout = connectTimeout;
        resetRequestConfig();
    }

   

    private void resetRequestConfig(){
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).
        		setConnectionRequestTimeout(requestTimeout).
        		setConnectTimeout(connectTimeout).
        		build();
    }

    /**
     * 允许商户自己做更高级更复杂的请求器配置
     *
     * @param requestConfig 设置HttpsRequest的请求器配置
     */
    public void setRequestConfig(RequestConfig requestConfig) {
    	this.requestConfig = requestConfig;
    }
    
    public void close(){
    	try {
    		log.info("关闭httpclient连接池：{}",httpClient.hashCode());
			httpClient.close();
		} catch (IOException e) {
			log.error("关闭连接失败", e);
		}
    }
    
    /**
     * 用来管理httpclient，clear方法在关闭应用时清除关闭所有连接
     * 如果不这么做，在weblogic中重新部署WeChatConsole这个应用时，因为weblogic进程没有关闭，而上次在它上面启动的httpclient连接会一直存在
     * @author xys
     *
     */
    public static class ClientManager{
    	private static List<HttpClientRequest> clients=new ArrayList<HttpClientRequest>();
    	public static void addClient(HttpClientRequest client){
    		clients.add(client);
    	}
    	public static void clear(){
    		log.info("关闭{}个httpclient连接池",clients.size());
    		for(HttpClientRequest client:clients){
    			if(client!=null){
    				try {
    					client.close();
					} catch (Exception e) {
					}
    			}
    		}
    		//静态成员instance，由程序手动管理，防止下次重启应用时使用这同一个被关闭连接的对象
    		HttpClientRequest.instance=null;
    		clients.clear();
    	}
    }
}
